<template>
    <view class="container">
        <view class="page-section">
            <view class="devices-summary">SDK打印版本号 {{ version }}</view>
            <view class="devices-summary">已发现 {{ devices.length }} 个外围设备：</view>
            <scroll-view class="device-list" scroll-y scroll-with-animation>
                <view
                    :data-device-id="item.deviceId"
                    :data-name="item.name || item.localName"
                    @tap="createBLEConnection"
                    class="device-item"
                    hover-class="device-item-hover"
                    v-for="(item, index) in devices"
                    :key="index"
                >
                    <view style="font-size: 16px; color: #333">{{ item.name }}</view>

                    <view style="font-size: 10px">信号强度: {{ item.RSSI }}dBm ({{ utils.max(0, item.RSSI + 100) }}%)</view>

                    <view style="font-size: 10px">UUID: {{ item.deviceId }}</view>

                    <view style="font-size: 10px">Service数量: {{ utils.len(item.advertisServiceUUIDs) }}</view>
                </view>
            </scroll-view>
            <view class="btn-area">
                <button type="primary" @tap="openBluetoothAdapter">开始扫描</button>
                <button @tap="stopBluetoothDevicesDiscovery" style="margin-top: 10px">停止扫描</button>
            </view>
        </view>
        <view class="page-section connected-area" v-if="lastDevice">
            <text style="font-size: 30rpx">最近连接的设备</text>
            <view>{{ lastDevice }}</view>
            <view class="btn-area">
                <button type="primary" @tap="createBLEConnectionWithDeviceId">直接连接</button>
            </view>
        </view>
        <view class="page-section connected-area" v-if="connected">
            <view class="connected-info">
                <text style="font-size: 30rpx">已连接到 {{ name }}</text>
                <view v-for="(item, index) in chs" :key="index">
                    <view>特性UUID: {{ item.uuid }}</view>

                    <view>特性值: {{ item.value }}</view>
                </view>
            </view>
            <view class="btn-area">
                <button v-if="canWrite" type="primary" @tap="writeBLECharacteristicValue" style="margin-bottom: 10px">文本打印测试</button>
                <button v-if="canQrcodWrite" type="primary" @tap="writeQrCodeBLECharacteristicValue" style="margin-bottom: 10px">二维码打印测试</button>
                <button @tap="closeBLEConnection">断开连接</button>
                <view style="margin-top: 4%; display: flex; flex-direction: row">
                    <canvas canvas-id="edit_area_canvas" :style="'width:' + codeSize + 'px;height:' + codeSize + 'px'"></canvas>
                </view>
            </view>
        </view>
    </view>
</template>
<script module="utils" lang="wxs">
module.exports.max = function (n1, n2) {
  return Math.max(n1, n2)
}
module.exports.len = function (arr) {
  arr = arr || [];
  return arr.length;
}
</script>
<script>
const LAST_CONNECTED_DEVICE = 'last_connected_device';
const PrinterJobs = require('../../printer/printerjobs');
const printerUtil = require('../../printer/printerutil');
const QR = require('../../utils/lib/qrcode');
const app = getApp();
function inArray(arr, key, val) {
    for (let i = 0; i < arr.length; i++) {
        if (arr[i][key] === val) {
            return i;
        }
    }
    return -1;
}

// ArrayBuffer转16进度字符串示例
function ab2hex(buffer) {
    const hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
        return ('00' + bit.toString(16)).slice(-2);
    });
    return hexArr.join(',');
}
export default {
    data() {
        return {
            devices: [],
            connected: false,
            chs: [],
            codeSize: 160,
            version: app.globalData.version,
            lastDevice: '',
            name: '',
            deviceId: '',
            canWrite: false,
            canQrcodWrite: false
        };
    },
    onUnload() {
        this.closeBluetoothAdapter();
    },
    onLoad(options) {
        const lastDevice = uni.getStorageSync(LAST_CONNECTED_DEVICE);
        this.setData({
            lastDevice: lastDevice
        });
    },
    methods: {
        openBluetoothAdapter() {
            if (!uni.openBluetoothAdapter) {
                uni.showModal({
                    title: '提示',
                    content: '当前微信版本过低，无法使用该功能，请升级到最新微信版本后重试。'
                });
                return;
            }
            uni.openBluetoothAdapter({
                success: (res) => {
                    console.log('openBluetoothAdapter success', res);
                    this.startBluetoothDevicesDiscovery();
                },
                fail: (res) => {
                    console.log('openBluetoothAdapter fail', res);
                    if (res.errCode === 10001) {
                        uni.showModal({
                            title: '错误',
                            content: '未找到蓝牙设备, 请打开蓝牙后重试。',
                            showCancel: false
                        });
                        uni.onBluetoothAdapterStateChange((res) => {
                            console.log('onBluetoothAdapterStateChange', res);
                            if (res.available) {
                                // 取消监听，否则stopBluetoothDevicesDiscovery后仍会继续触发onBluetoothAdapterStateChange，
                                // 导致再次调用startBluetoothDevicesDiscovery
                                uni.onBluetoothAdapterStateChange(() => {});
                                this.startBluetoothDevicesDiscovery();
                            }
                        });
                    }
                }
            });
            uni.onBLEConnectionStateChange((res) => {
                // 该方法回调中可以用于处理连接意外断开等异常情况
                console.log('onBLEConnectionStateChange', `device ${res.deviceId} state has changed, connected: ${res.connected}`);
                this.setData({
                    connected: res.connected
                });
                if (!res.connected) {
                    uni.showModal({
                        title: '错误',
                        content: '蓝牙连接已断开',
                        showCancel: false
                    });
                }
            });
        },

        getBluetoothAdapterState() {
            uni.getBluetoothAdapterState({
                success: (res) => {
                    console.log('getBluetoothAdapterState', res);
                    if (res.discovering) {
                        this.onBluetoothDeviceFound();
                    } else if (res.available) {
                        this.startBluetoothDevicesDiscovery();
                    }
                }
            });
        },

        startBluetoothDevicesDiscovery() {
            if (this._discoveryStarted) {
                return;
            }
            this._discoveryStarted = true;
            uni.startBluetoothDevicesDiscovery({
                success: (res) => {
                    console.log('startBluetoothDevicesDiscovery success', res);
                    this.onBluetoothDeviceFound();
                },
                fail: (res) => {
                    console.log('startBluetoothDevicesDiscovery fail', res);
                }
            });
        },

        stopBluetoothDevicesDiscovery() {
            uni.stopBluetoothDevicesDiscovery({
                complete: () => {
                    console.log('stopBluetoothDevicesDiscovery');
                    this._discoveryStarted = false;
                }
            });
        },

        onBluetoothDeviceFound() {
            uni.onBluetoothDeviceFound((res) => {
                res.devices.forEach((device) => {
                    if (!device.name && !device.localName) {
                        return;
                    }
                    const foundDevices = this.devices;
                    const idx = inArray(foundDevices, 'deviceId', device.deviceId);
                    const data = {};
                    if (idx === -1) {
                        data[`devices[${foundDevices.length}]`] = device;
                    } else {
                        data[`devices[${idx}]`] = device;
                    }
                    this.setData(data);
                });
            });
        },

        createBLEConnection(e) {
            const ds = e.currentTarget.dataset;
            const deviceId = ds.deviceId;
            const name = ds.name;
            this.createBLEConnectionFun(deviceId, name);
        },

        createBLEConnectionFun(deviceId, name) {
            uni.showLoading();
            uni.createBLEConnection({
                deviceId,
                success: () => {
                    console.log('createBLEConnection success');
                    this.setData({
                        connected: true,
                        name,
                        deviceId
                    });
                    this.getBLEDeviceServices(deviceId);
                    uni.setStorage({
                        key: LAST_CONNECTED_DEVICE,
                        data: name + ':' + deviceId
                    });
                },
                complete() {
                    uni.hideLoading();
                },
                fail: (res) => {
                    console.log('createBLEConnection fail', res);
                }
            });
            this.stopBluetoothDevicesDiscovery();
        },

        closeBLEConnection() {
            uni.closeBLEConnection({
                deviceId: this.deviceId
            });
            this.setData({
                connected: false,
                chs: [],
                canWrite: false,
                canQrcodWrite: false
            });
        },

        getBLEDeviceServices(deviceId) {
            uni.getBLEDeviceServices({
                deviceId,
                success: (res) => {
                    console.log('getBLEDeviceServices', res);
                    for (let i = 0; i < res.services.length; i++) {
                        if (res.services[i].isPrimary) {
                            this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid);
                            return;
                        }
                    }
                }
            });
        },

        getBLEDeviceCharacteristics(deviceId, serviceId) {
            uni.getBLEDeviceCharacteristics({
                deviceId,
                serviceId,
                success: (res) => {
                    console.log('getBLEDeviceCharacteristics success', res.characteristics);
                    // 这里会存在特征值是支持write，写入成功但是没有任何反应的情况
                    // 只能一个个去试
                    for (let i = 0; i < res.characteristics.length; i++) {
                        const item = res.characteristics[i];
						console.log('item===', item);
                        if (item.properties.write) {
                            this.setData({
                                canWrite: true,
                                canQrcodWrite: true
                            });
                            this._deviceId = deviceId;
                            this._serviceId = serviceId;
                            this._characteristicId = item.uuid;
                            break;
                        }
                    }
                },
                fail(res) {
                    console.error('getBLEDeviceCharacteristics', res);
                }
            });
        },

        writeBLECharacteristicValue() {
            let printerJobs = new PrinterJobs();
            let that = this;
            printerJobs
                .print('芯烨')
                .print('2020年09月22日17:34')
                .print(printerUtil.fillLine())
                .setAlign('ct')
                .setSize(2, 2)
                .print('#20饿了么外卖')
                .setSize(1, 1)
                .print('切尔西Chelsea')
                .setSize(2, 2)
                .print('在线支付(已支付)')
                .setSize(1, 1)
                .print('订单号：5415221202244734')
                .print('2020-09-07 18:08:08')
                .setAlign('lt')
                .print(printerUtil.fillAround('一号口袋'))
                .print(printerUtil.inline('意大利茄汁一面 * 1', '15'))
                .print(printerUtil.fillAround('其他'))
                .print('餐盒费：1')
                .print('[赠送康师傅冰红茶] * 1')
                .print(printerUtil.fillLine())
                .setAlign('rt')
                .print('原价：￥16')
                .print('总价：￥16')
                .setAlign('lt')
                .print(printerUtil.fillLine())
                .print('备注')
                .print('无')
                .print(printerUtil.fillLine())
                .printBar('12345678')
                .println();
            let buffer = printerJobs.buffer();
            console.log('ArrayBuffer', 'length: ' + buffer.byteLength, ' hex: ' + ab2hex(buffer));
            // 1.并行调用多次会存在写失败的可能性
            // 2.建议每次写入不超过20字节
            // 分包处理，延时调用
            const maxChunk = 20;
            for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {
                let subPackage = buffer.slice(i, i + maxChunk <= length ? i + maxChunk : length);
                that.writeBLECharacteristicValueFun(subPackage);
                // setTimeout(this._writeBLECharacteristicValue, 10, subPackage);//有问题时在加延迟
            }
        },

        writeQrCodeBLECharacteristicValue() {
            let printerJobs = new PrinterJobs();
            let that = this;
            //二维码打印测试
            printerJobs.print('芯烨二维码打印测试');
            printerJobs.setAlign('CT'); //设置二维码显示位置  居中
            QR.api.draw('https://www.xpyun.net/', 'edit_area_canvas', that.codeSize, that.codeSize, this, null);
            uni.canvasGetImageData({
                canvasId: 'edit_area_canvas',
                x: 0,
                y: 0,
                width: that.codeSize,
                height: that.codeSize,
                success: function (res) {
                    printerJobs.printQrcode(res).println();
                },
                complete: function (res) {
                    let buffer = printerJobs.buffer();
                    console.log('ArrayBuffer', 'length: ' + buffer.byteLength, ' hex: ' + ab2hex(buffer));
                    const maxChunk = 20;
                    for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {
                        let subPackage = buffer.slice(i, i + maxChunk <= length ? i + maxChunk : length);
                        that.writeBLECharacteristicValueFun(subPackage);
                    }
                }
            });
        },

        writeBLECharacteristicValueFun(buffer) {
            uni.writeBLECharacteristicValue({
                deviceId: this._deviceId,
                serviceId: this._serviceId,
                characteristicId: this._characteristicId,
                value: buffer,
                success(res) {
                    console.log('writeBLECharacteristicValue success', res);
                },
                fail(res) {
                    console.log('writeBLECharacteristicValue fail', res);
                }
            });
        },

        closeBluetoothAdapter() {
            uni.closeBluetoothAdapter();
            this._discoveryStarted = false;
        },

        createBLEConnectionWithDeviceId(e) {
            // 小程序在之前已有搜索过某个蓝牙设备，并成功建立连接，可直接传入之前搜索获取的 deviceId 直接尝试连接该设备
            const device = this.lastDevice;
            if (!device) {
                return;
            }
            const index = device.indexOf(':');
            const name = device.substring(0, index);
            const deviceId = device.substring(index + 1, device.length);
            console.log('createBLEConnectionWithDeviceId', name + ':' + deviceId);
            uni.openBluetoothAdapter({
                success: (res) => {
                    console.log('openBluetoothAdapter success', res);
                    this.createBLEConnectionFun(deviceId, name);
                },
                fail: (res) => {
                    console.log('openBluetoothAdapter fail', res);
                    if (res.errCode === 10001) {
                        uni.showModal({
                            title: '错误',
                            content: '未找到蓝牙设备, 请打开蓝牙后重试。',
                            showCancel: false
                        });
                        uni.onBluetoothAdapterStateChange((res) => {
                            console.log('onBluetoothAdapterStateChange', res);
                            if (res.available) {
                                // 取消监听
                                uni.onBluetoothAdapterStateChange(() => {});
                                this.createBLEConnectionFun(deviceId, name);
                            }
                        });
                    }
                }
            });
        }
    }
};
</script>
<style>
.page-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    box-sizing: border-box;
    border-bottom: 2rpx solid #eee;
    padding: 30rpx 0;
}

.devices-summary {
    padding: 10rpx;
    font-size: 30rpx;
}

.device-list {
    height: 300rpx;
}

.device-item {
    border-bottom: 1rpx solid #eee;
    padding: 10rpx;
    color: #666;
}

.device-item-hover {
    background-color: rgba(0, 0, 0, 0.1);
}

.btn-area {
    box-sizing: border-box;
    width: 100%;
    padding: 0 30rpx;
    margin: 30rpx 0;
}

.connected-area {
    font-size: 22rpx;
}

.connected-info {
}

.input-area {
    background: #fff;
    margin-top: 10rpx;
    width: 100%;
}

.input {
    font-size: 28rpx;
    height: 2.58823529em;
    min-height: 2.58823529em;
    line-height: 2.58823529em;
    padding: 10rpx;
}
</style>
